Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-2.5.0] Fix missing operationName in apollo-engine-reporting. #2557

Conversation

abernix
Copy link
Member

@abernix abernix commented Apr 10, 2019

The full-query caching feature implemented in #2437 originally took the additional steps of changing the apollo-engine-reporting module to utilize the new request pipeline available in Apollo Server as of #1795. (See also #2441, #2441 (comment))

While that would have been nice, there was still some uncertainty about some of the life-cycle hooks that would be necessary to make that happen and it wasn't worth blocking the implementation of full-query caching on those stalled decisions.

Therefore, the changes to utilize new functionality in the request pipeline, including what would have been a simplification of the way that apollo-engine-reporting would have obtained the operationName (something that is available via the API of the request-pipeline hooks), were backed out and will land separately in a future PR.

The portion regarding operationName was inadvertently not backed out and instead was left leveraging a life-cycle hook which was not available to the graphql-extensions API: didResolveOperation.

This means that the code was not setting operationName in the way it needed to and therefore operationName was always being left undefined (as is sometimes permitted!) with this new apollo-engine-reporting.

This commit puts the functionality back to that which is required for the graphql-extensions implementation, and the commit before this (8a43341) acts as a regression test (it should pass, as of this commit, and fail before it — see ❌ and ✅ on individual commits!).

abernix added 2 commits April 10, 2019 13:32
This should fail and then be fixed with an upcoming commit.
The full-query caching feature implemented in
#2437 originally took the
additional steps of also changing the `apollo-engine-reporting` module to
utilize the new request pipeline available in Apollo Server as of
#1795.

While that would have been nice, there was still some uncertainty about some
of the life-cycle hooks that would be necesssary to make that happen and it
wasn't worth blocking the implementation of full-query caching on those
stalled decisions.

Therefore, the changes to utilize new functionality in the request pipeline,
including what would have been a simplification of the way that
`apollo-engine-reporting` would have obtained the `operationName` (something
that is available via the API of the request-pipeline hooks), were backed
out and will land separately in a future PR.

The portion regarding `operationName` was inadvertently not backed out and
instead was left leveraging a life-cycle hook which was not available to the
`graphql-extensions` API: `didResolveOperation`.

This means that the code was not setting `operationName` in the way it
needed to and therefore `operationName` was always being left undefined (as
is sometimes permitted!) with this new `apollo-engine-reporting`.

This commit puts the functionality back to that which is required for the
`graphql-extensions` implementation, and the commit before this (8a43341)
acts as a regression test (it should pass, as of this commit, and fail
before it).
@abernix abernix requested a review from michael-watson April 10, 2019 12:39
@abernix abernix added this to the Release 2.5.0 milestone Apr 10, 2019
@abernix abernix merged commit d01086a into release-2.5.0 Apr 11, 2019
@abernix abernix deleted the abernix/reinstate-operationName-and-documentAST-via-executionDidStart branch April 11, 2019 11:45
@glasser
Copy link
Member

glasser commented Jun 21, 2019

Hmm. I actually think this is an inaccurate description of what happened in #2437. I'm moderately sure that I 100% intended to leave in the extension didResolveOperation API, and the mistake was just not invoking it in requestPipeline.

The PR description there did describe the motivation: the desire to register the operation with the stack before doing responseForOperation (for the cache). I see that since then this has been addressed by making the "requestDidEnd" callback hang on to the requestContext passed in to requestDidStart and observing that fields got added to it later, but that seems hackier than having an explicit callback at the right time (where we already have one in the new API).

This stuff is complex and I'm not upset that this got done while I was out, but I think it's worth at least recording my intentions for posterity.

@glasser
Copy link
Member

glasser commented Jun 21, 2019

Actually I think you were right to back the change out for a reason that'll be described in a change I'm making.

glasser added a commit that referenced this pull request Jun 21, 2019
The full query caching change (#2437) intended to introduce didResolveOperation
to the old graphql-extensions API used by apollo-engine-reporting ("backporting"
it from the newer plugin API). However, that change accidentally forgot to
invoke didResolveOperation from the request pipeline! This meant that the
operation name never got reported.

The change was backed out in #2557. But this unfortunately re-introduced the
exact bug that the change in #2437 was intended to fix: operationName was no
longer set when a result is served from the cache! Additionally, it was not set
if a *plugin* didResolveOperation call threw, which is what happens when the
operation registry plugin forbids an operation.

While we could have fixed this by reintroducing the didResolveOperation
extension API, there would be a subtle requirement that the
apollo-engine-reporting extension didResolveOperation be run before the
possibly-throwing operation registry didResolveOperation.

So instead, @abernix implemented #2711. This used `requestContext.operationName`
as a fallback if neither executionDidStart nor willResolveField gets
called. This will be set if the operation properly parsed, validates, and either
has a specified operationName that is found in the document, or there is no
specified operationName and there is exactly one operation in the document and
it has a name.

(Note that no version of this code ever sent the user-provided operationName in
case of parse or validation errors.)

The existing code is correct, but this PR cleans up a few things:

- #2557 reverted the one *implementation* of the didResolveOperation extension
   API, and #2437 accidentally didn't contain any *callers* of the API, but it
   was still declared on GraphQLExtension and GraphQLExtensionStack. This PR
   removes those declarations (which have never been useful).

- We currently look for the operation name in willResolveField. But in any case
  where fields are successfully being resolved, the pipeline must have managed
  to successfully resolve the operation and set requestContext.operationName. So
  we don't actually need the willResolveField code, because the "fallback" in
  the requestDidStart end-callback will have the same value. So take this code
  away. (This change is the motivation for this PR; for federation metrics I'm
  trying to disengage the "calculate times for fields" part of trace generation
  from the rest of it.)

- Fix the comment in "requestDidEnd" that implied incorrectly that
  requestContext.operationName was the user-provided name rather than the
  pipeline-calculated name. Be explicit both there and in requestPipeline.ts
  that we are relying on the fact that the RequestContext passed to
  requestDidStart is mutated to add operationName before its end handler is
  called.

This change is intended to be a no-op change (other than the removal of the
never-used APIs).
glasser added a commit that referenced this pull request Jun 21, 2019
The full query caching change (#2437) intended to introduce didResolveOperation
to the old graphql-extensions API used by apollo-engine-reporting ("backporting"
it from the newer plugin API). However, that change accidentally forgot to
invoke didResolveOperation from the request pipeline! This meant that the
operation name never got reported.

The change was backed out in #2557. But this unfortunately re-introduced the
exact bug that the change in #2437 was intended to fix: operationName was no
longer set when a result is served from the cache! Additionally, it was not set
if a *plugin* didResolveOperation call threw, which is what happens when the
operation registry plugin forbids an operation.

While we could have fixed this by reintroducing the didResolveOperation
extension API, there would be a subtle requirement that the
apollo-engine-reporting extension didResolveOperation be run before the
possibly-throwing operation registry didResolveOperation.

So instead, @abernix implemented #2711. This used `requestContext.operationName`
as a fallback if neither executionDidStart nor willResolveField gets
called. This will be set if the operation properly parsed, validates, and either
has a specified operationName that is found in the document, or there is no
specified operationName and there is exactly one operation in the document and
it has a name.

(Note that no version of this code ever sent the user-provided operationName in
case of parse or validation errors.)

The existing code is correct, but this PR cleans up a few things:

- #2557 reverted the one *implementation* of the didResolveOperation extension
   API, and #2437 accidentally didn't contain any *callers* of the API, but it
   was still declared on GraphQLExtension and GraphQLExtensionStack. This PR
   removes those declarations (which have never been useful).

- We currently look for the operation name in willResolveField. But in any case
  where fields are successfully being resolved, the pipeline must have managed
  to successfully resolve the operation and set requestContext.operationName. So
  we don't actually need the willResolveField code, because the "fallback" in
  the requestDidStart end-callback will have the same value. So take this code
  away. (This change is the motivation for this PR; for federation metrics I'm
  trying to disengage the "calculate times for fields" part of trace generation
  from the rest of it.)

- Fix the comment in "requestDidEnd" that implied incorrectly that
  requestContext.operationName was the user-provided name rather than the
  pipeline-calculated name. Be explicit both there and in requestPipeline.ts
  that we are relying on the fact that the RequestContext passed to
  requestDidStart is mutated to add operationName before its end handler is
  called.

This change is intended to be a no-op change (other than the removal of the
never-used APIs).
glasser added a commit that referenced this pull request Jun 24, 2019
The full query caching change (#2437) intended to introduce didResolveOperation
to the old graphql-extensions API used by apollo-engine-reporting ("backporting"
it from the newer plugin API). However, that change accidentally forgot to
invoke didResolveOperation from the request pipeline! This meant that the
operation name never got reported.

The change was backed out in #2557. But this unfortunately re-introduced the
exact bug that the change in #2437 was intended to fix: operationName was no
longer set when a result is served from the cache! Additionally, it was not set
if a *plugin* didResolveOperation call threw, which is what happens when the
operation registry plugin forbids an operation.

While we could have fixed this by reintroducing the didResolveOperation
extension API, there would be a subtle requirement that the
apollo-engine-reporting extension didResolveOperation be run before the
possibly-throwing operation registry didResolveOperation.

So instead, @abernix implemented #2711. This used `requestContext.operationName`
as a fallback if neither executionDidStart nor willResolveField gets
called. This will be set if the operation properly parsed, validates, and either
has a specified operationName that is found in the document, or there is no
specified operationName and there is exactly one operation in the document and
it has a name.

(Note that no version of this code ever sent the user-provided operationName in
case of parse or validation errors.)

The existing code is correct, but this PR cleans up a few things:

- #2557 reverted the one *implementation* of the didResolveOperation extension
   API, and #2437 accidentally didn't contain any *callers* of the API, but it
   was still declared on GraphQLExtension and GraphQLExtensionStack. This PR
   removes those declarations (which have never been useful).

- We currently look for the operation name in willResolveField. But in any case
  where fields are successfully being resolved, the pipeline must have managed
  to successfully resolve the operation and set requestContext.operationName. So
  we don't actually need the willResolveField code, because the "fallback" in
  the requestDidStart end-callback will have the same value. So take this code
  away. (This change is the motivation for this PR; for federation metrics I'm
  trying to disengage the "calculate times for fields" part of trace generation
  from the rest of it.)

- Fix the comment in "requestDidEnd" that implied incorrectly that
  requestContext.operationName was the user-provided name rather than the
  pipeline-calculated name. Be explicit both there and in requestPipeline.ts
  that we are relying on the fact that the RequestContext passed to
  requestDidStart is mutated to add operationName before its end handler is
  called.

This change is intended to be a no-op change (other than the removal of the
never-used APIs).
glasser added a commit that referenced this pull request Jun 24, 2019
The full query caching change (#2437) intended to introduce didResolveOperation
to the old graphql-extensions API used by apollo-engine-reporting ("backporting"
it from the newer plugin API). However, that change accidentally forgot to
invoke didResolveOperation from the request pipeline! This meant that the
operation name never got reported.

The change was backed out in #2557. But this unfortunately re-introduced the
exact bug that the change in #2437 was intended to fix: operationName was no
longer set when a result is served from the cache! Additionally, it was not set
if a *plugin* didResolveOperation call threw, which is what happens when the
operation registry plugin forbids an operation.

While we could have fixed this by reintroducing the didResolveOperation
extension API, there would be a subtle requirement that the
apollo-engine-reporting extension didResolveOperation be run before the
possibly-throwing operation registry didResolveOperation.

So instead, @abernix implemented #2711. This used `requestContext.operationName`
as a fallback if neither executionDidStart nor willResolveField gets
called. This will be set if the operation properly parsed, validates, and either
has a specified operationName that is found in the document, or there is no
specified operationName and there is exactly one operation in the document and
it has a name.

(Note that no version of this code ever sent the user-provided operationName in
case of parse or validation errors.)

The existing code is correct, but this PR cleans up a few things:

- #2557 reverted the one *implementation* of the didResolveOperation extension
   API, and #2437 accidentally didn't contain any *callers* of the API, but it
   was still declared on GraphQLExtension and GraphQLExtensionStack. This PR
   removes those declarations (which have never been useful).

- We currently look for the operation name in willResolveField. But in any case
  where fields are successfully being resolved, the pipeline must have managed
  to successfully resolve the operation and set requestContext.operationName. So
  we don't actually need the willResolveField code, because the "fallback" in
  the requestDidStart end-callback will have the same value. So take this code
  away. (This change is the motivation for this PR; for federation metrics I'm
  trying to disengage the "calculate times for fields" part of trace generation
  from the rest of it.)

- Fix the comment in "requestDidEnd" that implied incorrectly that
  requestContext.operationName was the user-provided name rather than the
  pipeline-calculated name. Be explicit both there and in requestPipeline.ts
  that we are relying on the fact that the RequestContext passed to
  requestDidStart is mutated to add operationName before its end handler is
  called.

This change is intended to be a no-op change (other than the removal of the
never-used APIs).
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants